/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *	  http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.milib.service {
	import com.googlecode.milib.base.MLActionClass;

	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.net.URLRequest;
	import flash.system.LoaderContext;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;

	/**
	 * @author Marek Brun
	 */
	public class MLLoader extends MLActionClass {

		//DATA:	Event
		/*
		Dispatched when data has loaded successfully.
		The complete event is always dispatched after the init event. 
		
		Defines the value of the type property of a complete event object. 
		This event has the following properties:
		
		bubbles false 
		cancelable false; there is no default behavior to cancel. 
		currentTarget The object that is actively processing the Event object with an event listener. 
		target The network object that has completed loading.  
		*/
		public const event_Complete : String = 'Complete';
		
		//DATA:	HTTPStatusEvent
		/*
		Dispatched when a network request is made over HTTP and Flash Player
		can detect the HTTP status code. 
		
		Defines the value of the type property of a httpStatus event object. 
		This event has the following properties:
		
		bubbles false 
		cancelable false; there is no default behavior to cancel. 
		currentTarget The object that is actively processing the Event object with an event listener. 
		status The HTTP status code returned by the server. 
		target The network object receiving an HTTP status code.
		*/
		public const event_HTTPStatus : String = 'HTTPStatus';
		
		//DATA:	Event
		/*
		Dispatched when the properties and methods of a loaded SWF file are accessible. A LoaderInfo
		object dispatches the init event when the following two conditions exist: 
		
		All properties and methods associated with the loaded object and those associated
		with the LoaderInfo object are accessible. 
		The constructors for all child objects have completed. 
		The init event always precedes the complete event.
		
		
		Defines the value of the type property of an init event object. 
		This event has the following properties:
		
		bubbles false 
		cancelable false; there is no default behavior to cancel. 
		currentTarget The object that is actively processing the Event object with an event listener. 
		target The LoaderInfo object associated with the SWF file being loaded. 
		*/
		public const event_Init : String = 'Init';
		
		//DATA:	IOErrorEvent
		/*
		Dispatched when an input or output error occurs that causes a load operation to fail. 
		
		Defines the value of the type property of an ioError event object. 
		This event has the following properties:
		
		bubbles false 
		cancelable false; there is no default behavior to cancel. 
		currentTarget The object that is actively processing the Event object with an event listener. 
		target The network object experiencing the input/output error. 
		text Text to be displayed as an error message. 
		*/
		public const event_IOError : String = 'IOError';
		
		//DATA:	Event
		/*
		Dispatched when a load operation starts. 
		
		Defines the value of the type property of an open event object. 
		This event has the following properties:
		
		Property Value 
		bubbles false 
		cancelable false; there is no default behavior to cancel. 
		currentTarget The object that is actively processing the Event object with an event listener. 
		target The network object that has opened a connection.  
		*/
		public const event_Open : String = 'Open';
		
		//DATA:	ProgressEvent
		/*
		Dispatched periodically during the file upload or download operation.
		The progress event is dispatched while Flash Player transmits bytes
		to a server, and it is periodically dispatched during the transmission,
		even if the transmission is ultimately not successful. To determine if
		and when the file transmission is actually successful and complete,
		listen for the complete event. 
		
		In some cases, progress events are not received. For example, when the file
		being transmitted is very small or the upload or download happens very quickly
		a progress event might not be dispatched.
		
		File upload progress cannot be determined on Macintosh platforms earlier than
		OS X 10.3. The progress event is called during the upload operation, but the
		value of the bytesLoaded property of the progress event is -1, indicating that
		the progress cannot be determined.
		
		Defines the value of the type property of a progress event object. 
		This event has the following properties:
		 
		bubbles false 
		bytesLoaded The number of items or bytes loaded at the time the listener processes the event. 
		bytesTotal The total number of items or bytes that ultimately will be loaded if the loading process succeeds. 
		cancelable false; there is no default behavior to cancel. 
		currentTarget The object that is actively processing the Event object with an event listener. 
		target The network object reporting progress.
		*/
		public const event_Progress : String = 'Progress';
		
		//DATA:	Event
		/*
		Dispatched by a LoaderInfo object whenever a loaded object is removed by using the unload()
		method of the Loader object, or when a second load is performed by the same Loader object
		and the original content is removed prior to the load beginning.
		
		Defines the value of the type property of an unload event object.
		
		bubbles false 
		cancelable false; there is no default behavior to cancel. 
		currentTarget The object that is actively processing the Event object with an event listener. 
		target The LoaderInfo object associated with the SWF file being unloaded or replaced.
		*/ 
		public const event_Unload : String = 'Unload';
		
		
		private static const servicedObjects : Dictionary = new Dictionary(true);
		private static const urlToRequest : Dictionary = new Dictionary(true);
		public var loader:Loader;
		private var request:URLRequest;
		private var context:LoaderContext;
		private var progress:Number;		

		
		/*
Creates a Loader object that you can use to load files, such as SWF, JPEG, GIF, or PNG files.
Call the load() method to load the asset as a child of the Loader instance. You can then add
the Loader object to the display list (for instance, by using the addChild() method
of a DisplayObjectContainer instance). The asset appears on the Stage as it loads. 

You can also use a Loader instance "offlist," that is without adding it to a display object
container on the display list. In this mode, the Loader instance might be used to load a SWF
file that contains additional modules of an application. 

To detect when the SWF file is finished loading, you can use the events of the LoaderInfo
object associated with the contentLoaderInfo property of the Loader object. At that point,
the code in the module SWF file can be executed to initialize and start the module. In the
offlist mode, a Loader instance might also be used to load a SWF file that contains components
or media assets. Again, you can use the LoaderInfo object event notifications to detect when
the components is finished loading. At that point, the application can start using the components
and media assets in the library of the SWF file by instantiating the ActionScript 3.0 classes that
represent those components and assets.

To determine the status of a Loader object, monitor the following events that the LoaderInfo
object associated with the contentLoaderInfo property of the Loader object:
 - The open event is dispatched when loading begins. 
 - The ioError or securityError event is dispatched if the file cannot be loaded or if an error
   occured during the load process. 
 - The progress event fires continuously while the file is being loaded. 
 - The complete event is dispatched when a file completes downloading, but before the loaded
   movie clip's methods and properties are available. 
 - The init event is dispatched after the properties and methods of the loaded SWF file are accessible,
   so you can begin manipulating the loaded SWF file. This event is dispatched before the complete
   handler. In streaming SWF files, the init event can occur significantly earlier than the complete
   event. For most purposes, use the init handler. 
*/
		public function MLLoader() {
			loader=new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete, false, 0, true);
			loader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, onLoaderHttpStatus, false, 0, true);
			loader.contentLoaderInfo.addEventListener(Event.INIT, onLoaderInit, false, 0, true);
			loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onLoaderIOError, false, 0, true);
			loader.contentLoaderInfo.addEventListener(Event.OPEN, onLoaderOPEN, false, 0, true);
			loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoaderProgress, false, 0, true);
			loader.contentLoaderInfo.addEventListener(Event.UNLOAD, onLoaderUnload, false, 0, true);
			
		}
		
		public function getProgress():Number {
			return progress;
		}

		/*
<p>Loads a SWF, JPEG, progressive JPEG, unanimated GIF, or PNG file into an object that is a child
of this Loader object. If you load an animated GIF file, only the first frame is displayed. As the
Loader object can contain only a single child, issuing a subsequent load() request terminates the
previous request, if still pending, and commences a new load. 

A SWF file or image loaded into a Loader object inherits the position, rotation, and scale properties
of the parent display objects of the Loader object. 

Use the unload() method to remove movies or images loaded with this method, or to cancel a load operation
that is in progress.

When you use the load() method, consider the Flash Player security model: 

 - You can load content from any accessible source. 
 - Loading is not allowed if the calling SWF file is in a network sandbox and the file to be loaded
   is local. 
 - If the loaded content is a SWF file, it cannot be scripted by a SWF file in another security
   sandbox unless that cross-scripting arrangement was approved through a call to the Security.allowDomain() method in the loaded content file. 
 - SWF files written in ActionScript 1.0 or 2.0, which are loaded as AVM1Movie objects, cannot
   cross-script SWF files written in ActionScript 3.0, which are loaded as Sprite or MovieClip objects.
   You can use the LocalConnection class to have these files communicate with each other. 
 - If the loaded content is an image, its data cannot be accessed by a SWF file outside of the security
   sandbox, unless the domain of that SWF file was included a cross-domain policy file at the origin domain of the image. 
 - Movie clips in the local-with-file-system sandbox cannot cross-script movie clips in the
   local-with-networking sandbox, and the reverse is also prevented. 
 - You can prevent a SWF file from using this method by setting the allowNetworking parameter
   of the the object and embed tags in the HTML page that contains the SWF content.</p> 
 


 @param request — The absolute or relative URL of the SWF, JPEG, GIF, or PNG file to be loaded.
   A relative path must be relative to the main SWF file. Absolute URLs must include the protocol
   reference, such as http:// or file:///. Filenames cannot include disk drive specifications.  
  
 @param context — A LoaderContext object, which has properties that define the following: 
	- Whether or not Flash Player should check for the existence of a policy file upon loading the object 
	- The ApplicationDomain for the loaded object 
	- The SecurityDomain for the loaded object
	
 @throws SecurityError — The value of LoaderContext.securityDomain must be either null
 or SecurityDomain.currentDomain. This reflects the fact that you can only place the loaded
 media in its natural security sandbox or your own (the latter requires a policy file).  
  
 @throws SecurityError — Local SWF files may not set LoaderContext.securityDomain to anything
 other than null. It is not permitted to import non-local media into a local sandbox, or to
 place other local media in anything other than its natural sandbox.  
  
 @throws SecurityError — If the applicationDomain or securityDomain properties of the context
 parameter are from a disallowed domain.  
  
 @throws SecurityError — If a local SWF file is attempting to use the securityDomain property
 of the context parameter.
		 */
		private function load(request:URLRequest, context:LoaderContext=null):void {
			loader.load(request, context);
		}
		
		public function setup(request:URLRequest, context:LoaderContext=null):void {
			if(!request.url){
				throw new Error('Passed request contain null url');
			}
			this.request=request;
			this.context=context;
		}
		
		override protected function doStart():Boolean {
			if(request) {
				_isSuccess=false;
				progress=0;
				load(request, context);
				return true;
			}else{
				throw new Error('Please setup first with method "setup"');
			}
		}
		
		/**
<p> Cancels a load() method operation that is currently in progress for the Loader instance. </p>
		 */
		public function close():void {
			loader.close();
		}
		
		/**
<p>Loads from binary data stored in a ByteArray object.</p> 

 @param bytes — A ByteArray object. The contents of the ByteArray can be any of the file
 formats supported by the Loader class: SWF, GIF, JPEG, or PNG.  
  
 @param context — A LoaderContext object. Only the applicationDomain property of the
 LoaderContext object applies; the checkPolicyFile and securityDomain properties
 of the LoaderContext object do not apply.  

 @throws ArgumentError — If the length property of the ByteArray object is not greater than 0.  
  
 @throws IllegalOperationError — If the checkPolicyFile or securityDomain property
 of the context parameter are non-null.  
  
 @throws SecurityError — If the provided applicationDomain property of the context property
 is from a disallowed domain.  
 		*/
 		public function loadBytes(bytes:ByteArray, context:LoaderContext = null):void {
 			try{
 				loader.loadBytes(bytes, context);
 			}catch(e:*){
 				throw e;
 			}
 		}
 		
 		/**
<p>Removes a child of this Loader object that was loaded by using the load() method.
The property of the associated LoaderInfo object is reset to null. The child is not
necessarily destroyed because other objects might have references to it; however,
it is no longer a child of the Loader object. 

As a best practice, before you unload a child SWF file, you should explicitly close
any streams in the child SWF file's objects, such as LocalConnection, NetConnection,
NetStream, and Sound objects. Otherwise, audio in the child SWF file might continue
to play, even though the child SWF file was unloaded. To close streams in the child
SWF file, add an event listener to the child that listens for the unload event. When
the parent calls Loader.unload(), the unload event is dispatched to the child. The
following code shows how you might do this:

function closeAllStreams(evt:Event) { 
	myNetStream.close();
	mySound.close();
	myNetConnection.close();
	myLocalConnection.close();
}

myMovieClip.loaderInfo.addEventListener(Event.UNLOAD, closeAllStreams);</p>
 		*/
 		public function unload():void {
 			loader.unload();
 		}
 		
		public static function forInstance(request:URLRequest):MLLoader {
			if(servicedObjects[request]){
				return servicedObjects[request];
			}else{
				servicedObjects[request]=new MLLoader();
				MLLoader(servicedObjects[request]).setup(request);
				return servicedObjects[request];
			}
		}
		
		public static function forInstanceAsString(url:String):MLLoader {
			if(!urlToRequest[url]){
				urlToRequest[url]=new URLRequest(url);
			}
			return forInstance(urlToRequest[url]);
		}
		
		public function getURL():String {
			return request.url;
		}
		
//********************************************************************************************
//		events for MLLoader
//********************************************************************************************
		protected function onLoaderComplete(event:Event):void {
			_isSuccess=true;
			progress=1;
			broadcastEvent(event_Complete, event);
			finish();
		}
		
		protected function onLoaderHttpStatus(event:HTTPStatusEvent):void {
			broadcastEvent(event_HTTPStatus, event);
		}
		
		protected function onLoaderInit(event:Event):void {
			broadcastEvent(event_Init, event);
		}
		
		protected function onLoaderIOError(event:IOErrorEvent):void {
			broadcastEvent(event_IOError, event);
			finish();
		}
		
		protected function onLoaderOPEN(event:Event):void {
			broadcastEvent(event_Open, event);
		}
		
		protected function onLoaderProgress(event:ProgressEvent):void {
			progress=event.bytesLoaded/event.bytesTotal;
			broadcastEvent(event_Progress, event);
		}
		
		protected function onLoaderUnload(event:Event):void {
			broadcastEvent(event_Unload, event);
			finish();
		}
	}
}
